#include <stdio.h>                  // 标准输入输出
#include <unistd.h>                 // POSIX标准接口

// 字符分类函数库
// C character classification functions，用于测试字符是否属于特定的字符类别，如字母字符、控制字符等等
#include <ctype.h>

#include "ohos_init.h"              // 用于初始化服务(services)和功能(features)
#include "cmsis_os2.h"              // CMSIS-RTOS API V2

#include "ssd1306.h"                // OLED驱动接口
#include "ssd1306_tests.h"          // 测试集接口

// 定义一幅二维码图像
// 定义二维码的宽度和高度
const unsigned char qrSize[] = { 64, 64 };
// 定义二维码的数据，由img2code.py生成
const unsigned char qrData[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x79, 0x86, 0x79, 0x9E, 0x00, 0x07,
0xE0, 0x00, 0x79, 0x86, 0x79, 0x9E, 0x00, 0x07, 0xE3, 0xFE, 0x66, 0x19, 0xF9, 0x9E, 0x3F, 0xE7,
0xE7, 0xFE, 0x66, 0x19, 0xF9, 0x9E, 0x7F, 0xE7, 0xE6, 0x06, 0x7F, 0xFF, 0x9F, 0x9E, 0x60, 0x67,
0xE6, 0x06, 0x7F, 0xFF, 0x9F, 0x9E, 0x60, 0x67, 0xE6, 0x06, 0x7F, 0x99, 0xE7, 0xFE, 0x60, 0x67,
0xE6, 0x06, 0x7F, 0x99, 0xE7, 0xFE, 0x60, 0x67, 0xE6, 0x06, 0x66, 0x79, 0xE0, 0x1E, 0x60, 0x67,
0xE6, 0x06, 0x66, 0x79, 0xE0, 0x1E, 0x60, 0x67, 0xE7, 0xFE, 0x78, 0x66, 0x06, 0x06, 0x7F, 0xE7,
0xE7, 0xFE, 0x78, 0x66, 0x06, 0x06, 0x7F, 0xE7, 0xE0, 0x00, 0x66, 0x66, 0x66, 0x66, 0x00, 0x07,
0xE0, 0x00, 0x66, 0x66, 0x66, 0x66, 0x00, 0x07, 0xFF, 0xFF, 0xF8, 0x67, 0x99, 0xE7, 0xFF, 0xFF,
0xFF, 0xFF, 0xF8, 0x67, 0x99, 0xE7, 0xFF, 0xFF, 0xE6, 0x66, 0x79, 0x9F, 0xE1, 0x87, 0xE7, 0x9F,
0xE6, 0x66, 0x79, 0x9F, 0xE1, 0x87, 0xE7, 0x9F, 0xE1, 0x99, 0xE7, 0xE1, 0x81, 0xE0, 0x79, 0xE7,
0xE1, 0x99, 0xE7, 0xE1, 0x81, 0xE0, 0x79, 0xE7, 0xF8, 0x00, 0x18, 0x66, 0x00, 0x79, 0xFE, 0x07,
0xF8, 0x00, 0x18, 0x66, 0x00, 0x79, 0xFE, 0x07, 0xE1, 0x81, 0xE1, 0xF8, 0x60, 0x06, 0x67, 0x9F,
0xE1, 0x81, 0xE1, 0xF8, 0x60, 0x06, 0x67, 0x9F, 0xFE, 0x18, 0x18, 0x1E, 0x19, 0x80, 0x79, 0x87,
0xFE, 0x18, 0x18, 0x1E, 0x19, 0x80, 0x79, 0x87, 0xFE, 0x07, 0x9F, 0x9E, 0x1F, 0xE6, 0x19, 0xE7,
0xFE, 0x07, 0x9F, 0x9E, 0x1F, 0xE6, 0x19, 0xE7, 0xE7, 0x9E, 0x7E, 0x01, 0xF9, 0xE6, 0x19, 0x87,
0xE7, 0x9E, 0x7E, 0x01, 0xF9, 0xE6, 0x19, 0x87, 0xF9, 0x99, 0xE1, 0xE7, 0x99, 0xF9, 0xF9, 0x9F,
0xF9, 0x99, 0xE1, 0xE7, 0x99, 0xF9, 0xF9, 0x9F, 0xF9, 0x98, 0x01, 0x87, 0xE0, 0x78, 0x19, 0x87,
0xF9, 0x98, 0x01, 0x87, 0xE0, 0x78, 0x19, 0x87, 0xFF, 0x87, 0xE1, 0x99, 0x87, 0xE0, 0x78, 0x67,
0xFF, 0x87, 0xE1, 0x99, 0x87, 0xE0, 0x78, 0x67, 0xE6, 0x60, 0x18, 0x66, 0x01, 0xF8, 0x1F, 0x87,
0xE6, 0x60, 0x18, 0x66, 0x01, 0xF8, 0x1F, 0x87, 0xF9, 0xF9, 0xE7, 0xE0, 0x60, 0x1F, 0xF9, 0x9F,
0xF9, 0xF9, 0xE7, 0xE0, 0x60, 0x1F, 0xF9, 0x9F, 0xE6, 0x78, 0x61, 0xE6, 0x1E, 0x60, 0x07, 0xFF,
0xE6, 0x78, 0x61, 0xE6, 0x1E, 0x60, 0x07, 0xFF, 0xFF, 0xFF, 0xE7, 0x86, 0x1F, 0x87, 0xE6, 0x07,
0xFF, 0xFF, 0xE7, 0x86, 0x1F, 0x87, 0xE6, 0x07, 0xE0, 0x00, 0x79, 0x81, 0xF9, 0x86, 0x61, 0x87,
0xE0, 0x00, 0x79, 0x81, 0xF9, 0x86, 0x61, 0x87, 0xE3, 0xFE, 0x7E, 0x07, 0x9F, 0xE7, 0xE1, 0xE7,
0xE7, 0xFE, 0x7E, 0x07, 0x9F, 0xE7, 0xE1, 0xE7, 0xE6, 0x06, 0x61, 0x87, 0xE7, 0xE0, 0x07, 0xFF,
0xE6, 0x06, 0x61, 0x87, 0xE7, 0xE0, 0x07, 0xFF, 0xE6, 0x06, 0x7E, 0x7F, 0x81, 0x9F, 0xE6, 0x7F,
0xE6, 0x06, 0x7E, 0x7F, 0x81, 0x9F, 0xE6, 0x7F, 0xE6, 0x06, 0x67, 0x86, 0x06, 0x7F, 0x81, 0xE7,
0xE6, 0x06, 0x67, 0x86, 0x06, 0x7F, 0x81, 0xE7, 0xE7, 0xFE, 0x7F, 0xF8, 0x60, 0x06, 0x67, 0x9F,
0xE7, 0xFE, 0x7F, 0xF8, 0x60, 0x06, 0x67, 0x9F, 0xE0, 0x00, 0x60, 0x60, 0x1E, 0x07, 0x81, 0x87,
0xE0, 0x00, 0x60, 0x60, 0x1E, 0x07, 0x81, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};


/// @brief 显示汉字，16x16，宋体
void TestDrawChinese1(void)
{
    // 文字宽高16x16
    const uint32_t W = 16, H = 16;
    
    // [取模工具]: https://www.23bei.com/tool-223.html
    // [输出格式]: C51	
    // [数据排列]: 从左到右从上到下 
    // [取模方式]: 横向8点左高位 (显示的时候使用ssd1306_DrawRegion函数，在指定区域绘制内容，按行绘制)
    // [黑白取反]: 正常
    // [字体种类]: HZK1616宋体
    // [强制全角]: ASCII自动转全角
    // [总字符库]: 万 物 互 联
    // 使用二维数组，每个字32个字节，根据取模方式，每个字的前16字节是汉字的上半部分，后16字节是汉字的下半部分
    uint8_t fonts[][32] = {
        {
            /*-- ID:0,字符:"万",ASCII编码:CDF2,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/
            0x00,0x00,0x00,0x04,0xFF,0xFE,0x02,0x00,0x02,0x00,0x02,0x10,0x03,0xF8,0x02,0x10,
            0x02,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x08,0x10,0x10,0x10,0x20,0xA0,0x40,0x40,
        },{
            /*-- ID:1,字符:"物",ASCII编码:CEEF,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/
            0x11,0x00,0x11,0x00,0x51,0x00,0x51,0x04,0x7B,0xFE,0x54,0xA4,0x90,0xA4,0x1C,0xA4,
            0x31,0x24,0xD1,0x44,0x12,0x44,0x12,0x44,0x14,0x84,0x10,0x84,0x11,0x28,0x10,0x10,
        },{
            /*-- ID:2,字符:"互",ASCII编码:BBA5,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/
            0x00,0x08,0x7F,0xFC,0x08,0x00,0x08,0x20,0x0F,0xF0,0x08,0x20,0x08,0x20,0x08,0x20,
            0x08,0x20,0x10,0x20,0x1F,0xE0,0x00,0x20,0x00,0x20,0x00,0x24,0xFF,0xFE,0x00,0x00,
        },{
            /*-- ID:3,字符:"联",ASCII编码:C1AA,对应字:宽x高=16x16,画布:宽W=16 高H=16,共32字节*/
            0x01,0x04,0xFE,0xCC,0x24,0x50,0x24,0x00,0x3D,0xFC,0x24,0x20,0x24,0x20,0x24,0x24,
            0x3F,0xFE,0x24,0x20,0x24,0x20,0x24,0x50,0x3E,0x50,0xE4,0x88,0x05,0x0E,0x06,0x04
        }
    };

    // 全屏填充黑色
    ssd1306_Fill(Black);

    // 汉字逐字显示
    for (size_t i = 0; i < sizeof(fonts)/sizeof(fonts[0]); i++) {   //遍历每个汉字
        // 使用ssd1306_DrawRegion函数，在指定区域绘制内容，按行绘制
        // 参数x：左上顶点x坐标          i*W，汉字左上角x坐标，汉字间不留空隙
        // 参数y：左上顶点y坐标          0，屏幕顶端
        // 参数w：宽度（像素）           W，汉字宽度16
        // 参数h：高度（像素）           H，汉字高度16
        // 参数data：内容               fonts[i]，显示第几个字
        // 参数size：内容大小（字节）     sizeof(fonts[0])，每个字占用的字节数32
        // 参数stride：内容宽度（像素）   W，汉字宽度16
        ssd1306_DrawRegion(i * W, 0, W, H, fonts[i], sizeof(fonts[0]), W);
    }

    // 设置显示位置
    ssd1306_SetCursor(64, 0);
    // 显示二维码
    ssd1306_DrawRegion(64, 0, qrSize[0], qrSize[1], qrData, sizeof(qrData), qrSize[0]);

    // 上屏
    ssd1306_UpdateScreen();
}

/// @brief 显示汉字，12x12，宋体
void TestDrawChinese2(void)
{
    // 文字宽高12x12
    const uint32_t W = 12, H = 12;

    // 画布宽高16x12，所以需要为ssd1306_DrawRegion函数定义stride=16
    const uint32_t S = 16;

    // [取模工具]: https://www.23bei.com/tool-223.html
    // [输出格式]: C51	
    // [数据排列]: 从左到右从上到下 
    // [取模方式]: 横向8点左高位 (显示的时候使用ssd1306_DrawRegion函数，在指定区域绘制内容，按行绘制)
    // [黑白取反]: 正常
    // [字体种类]: HZK1212宋体
    // [强制全角]: ASCII自动转全角
    // [总字符库]: 万 物 互 联
    // 使用二维数组，每个字24个字节（存储宽度必须是8的倍数，所以字宽12实际上占用16位，16*12/8=24）
    // 根据取模方式，每个字的前12字节是汉字的上半部分，后12字节是汉字的下半部分
    uint8_t fonts[][24] = {
        {
            /*-- ID:0,字符:"万",ASCII编码:CDF2,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/
            0x00,0x20,0xFF,0xF0,0x04,0x00,0x04,0x00,0x07,0xC0,0x04,0x40,0x04,0x40,0x08,0x40,
            0x08,0x40,0x10,0x40,0x22,0x80,0xC1,0x00,
        },{
            /*-- ID:1,字符:"物",ASCII编码:CEEF,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/
            0x22,0x00,0x22,0x00,0xA3,0xF0,0xF5,0x50,0xA9,0x50,0xA1,0x50,0x3A,0x90,0xE4,0xA0,
            0x29,0x20,0x22,0x20,0x2D,0x20,0x20,0xC0,
        },{
            /*-- ID:2,字符:"互",ASCII编码:BBA5,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/
            0x00,0x20,0xFF,0xF0,0x08,0x00,0x08,0x00,0x1F,0x80,0x10,0x80,0x10,0x80,0x10,0x80,
            0x3F,0x80,0x01,0x00,0x01,0x20,0xFF,0xF0,
        },{
            /*-- ID:3,字符:"联",ASCII编码:C1AA,对应字:宽x高=12x12,画布:宽W=16 高H=12,共24字节*/
            0x04,0x60,0xFA,0x40,0x52,0x80,0x77,0xE0,0x51,0x00,0x7F,0xF0,0x51,0x00,0x59,0x00,
            0xF1,0x40,0x11,0x40,0x12,0x20,0x14,0x10
        }
    };

    // 全屏填充黑色
    ssd1306_Fill(Black);

    // 汉字逐字显示
    for (size_t i = 0; i < sizeof(fonts)/sizeof(fonts[0]); i++) {   //遍历每个汉字
        // 注意：虽然文字宽高12x12，但是画布宽高16x12，所以需要为ssd1306_DrawRegion函数定义stride=16
        ssd1306_DrawRegion(i * W, 0, W, H, fonts[i], sizeof(fonts[0]), S);
    }

    // 显示二维码
    ssd1306_SetCursor(64, 0);
    ssd1306_DrawRegion(64, 0, qrSize[0], qrSize[1], qrData, sizeof(qrData), qrSize[0]);

    // 显示第一行英文
    ssd1306_SetCursor(0, 64 - 10*2);
    ssd1306_DrawString("dragon", Font_7x10, White);

    // 显示第二行英文
    ssd1306_SetCursor(0, 64 - 10);
    ssd1306_DrawString("@China", Font_7x10, White);

    // 上屏
    ssd1306_UpdateScreen();
}

/// @brief 遍历ASCII码表，显示所有可打印西文字符
void TestShowChars(FontDef font, uint8_t w, uint8_t h)
{
    // 全屏填充黑色
    ssd1306_Fill(Black);

    // 光标初始位置
    uint8_t x = 0, y = 0;

    // 遍历ASCII码表
    for (uint8_t c = 1; c < 128; c++) {
        // 显示所有可打印西文字符
        if (isprint(c)) {   // 可打印字符
            // 设置显示位置
            ssd1306_SetCursor(x, y);
            // 显示一个字符
            ssd1306_DrawChar((char) c, font, White);
            // 向右移动光标
            x += w;
            // 光标到达屏幕右侧，换行（光标向下移动一行，回到最左侧）
            if (x >= SSD1306_WIDTH) {   //SSD1306_WIDTH在ssd1306.h中定义
                x = 0;
                y += h;
            }
        }
    }
    // 上屏
    ssd1306_UpdateScreen();
}

// 主线程函数
void Ssd1306TestTask(void* arg)
{
    (void) arg;

    // 初始化OLED
    ssd1306_Init();

    // 全屏填充黑色
    ssd1306_Fill(Black);

    // 光标定位在左上角
    ssd1306_SetCursor(0, 0);

    // 显示文字
    ssd1306_DrawString("OpenHarmony!", Font_7x10, White);

    // 记录起始时间
    uint32_t start = HAL_GetTick();     // 返回内核经历了多少ms

    // 上屏
    ssd1306_UpdateScreen();

    // 记录结束时间
    uint32_t end = HAL_GetTick();

    // 输出上屏耗时
    printf("ssd1306_UpdateScreen time cost: %d ms.\r\n", end - start);

    // 工作循环
    while (1) {
        // 显示汉字
        TestDrawChinese1();
        osDelay(500);

        // 显示汉字
        TestDrawChinese2();
        osDelay(500);

        // 显示所有可打印西文字符
        TestShowChars(Font_6x8, 6, 8);
        osDelay(500);

        // 显示所有可打印西文字符
        TestShowChars(Font_7x10, 7, 10);
        osDelay(500);

        // 运行驱动自带的测试集
        // ssd1306_TestAll();
    }
}

// 入口函数
void Ssd1306TestDemo(void)
{
    // 定义线程属性
    osThreadAttr_t attr;
    attr.name = "Ssd1306Task";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = osPriorityNormal;

    // 创建线程
    if (osThreadNew(Ssd1306TestTask, NULL, &attr) == NULL) {
        printf("[Ssd1306TestDemo] Falied to create Ssd1306TestTask!\n");
    }
}

// 运行入口函数
APP_FEATURE_INIT(Ssd1306TestDemo);
